package cn.sanli.manage.security;

import cn.sanli.manage.pojo.entity.Log;
import cn.sanli.manage.service.LogService;
import cn.sanli.manage.web.JsonResult;
import cn.sanli.manage.web.ServiceCode;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import io.jsonwebtoken.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.Map;

@Slf4j
@Component
public class JwtAuthorizationFilter extends OncePerRequestFilter {

    @Value("${security.jwt}")
    private String signature;

    @Resource
    private LogService logService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        log.debug("JWT过滤器开始执行……");
        // 获取请求路径
        RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
        HttpServletRequest httpServletRequest = ((ServletRequestAttributes) requestAttributes).getRequest();
        // 获取请求的方法路径
        String url = httpServletRequest.getRequestURI();
        // 获取请求参数
        Map<String, String[]> reqParams = request.getParameterMap();
        //创建一个字符串数组，长度为请求参数的数量
        String[] reqParam = new String[reqParams.size()];
        //定义一个索引变量
        int index = 0;
        //遍历请求参数，将参数名和参数值拼接成字符串，存入reqParam数组中
        for (Map.Entry<String, String[]> entry : reqParams.entrySet()) {
            reqParam[index] = entry.getKey() + "=" + Arrays.toString(entry.getValue());
            index++;
        }
        log.info("reqParam的值【请求参数】为：" + Arrays.toString(reqParam));

        // 输出请求日志
        //获取请求头中的Authorization
        String jwt = request.getHeader("Authorization");
        //判断Authorization是否有文本
        if (!StringUtils.hasText(jwt)) {
            //如果没有文本，则执行过滤器链
            filterChain.doFilter(request, response);
            return;
        }
        Claims body = null;
        try {
            // 使用签名密钥解析jwt
            body = Jwts.parser().setSigningKey(signature).parseClaimsJws(jwt).getBody();
        } catch (MalformedJwtException e) {
            // jwt格式错误
            responseMessage(response, ServiceCode.ERROR_UNAUTHORIZED, "数字类型格式转换异常");
            return;
        } catch (SignatureException e) {
            // jwt签名错误
            responseMessage(response, ServiceCode.ERROR_JWT_SIGNATURE, "jwt数据验证失败");
            return;
        } catch (ExpiredJwtException e) {
            // jwt已过期
            responseMessage(response, ServiceCode.ERROR_JWT_EXPIRED, "jwt数据已过有效期，请重新登录操作");
            return;
        }
        // 从jwt中获取num、userName、authoritiesJsonString
        String num = body.get("num", String.class);
        String userName = body.get("userName", String.class);
        String authoritiesJsonString = body.get("authoritiesJsonString", String.class);
        Integer centerId = body.get("centerId", Integer.class);
        Integer deptId = body.get("deptId", Integer.class);
        Integer roleId = body.get("rol", Integer.class);
        System.out.println("roleId的值为"+roleId);
        // 创建LoginPrincipal对象
        LoginPrincipal loginPrincipal = new LoginPrincipal().setNum(num).setRoles(authoritiesJsonString).setUserName(userName).setRoleId(roleId).setDeptId(deptId).setCenterId(centerId);
        log.debug("自定义当事人信息是{}", loginPrincipal);
        //记录日志
        String operatorInfo = userName + "(" + authoritiesJsonString + ")";
        //记录操作时间
        Date runTime = new Date();
        log.info("request end, operatorId: {}, operatorInfo: {}, cost: {}ms", operatorInfo, num, runTime);
        //存储日志信息
        Log l = new Log().setOperatorId(num).setParams(Arrays.toString(reqParam)).setLogMessage(url).setOperatorInfo(operatorInfo).setStatus(0).setRunTime(runTime);
        logService.addLog(l);
        //解析完成后将正确信息存入到Security Context中
//将JSON字符串转换成SimpleGrantedAuthority对象集合
        Collection<SimpleGrantedAuthority> grantedAuthorities = JSON.parseArray(authoritiesJsonString, SimpleGrantedAuthority.class);
        //创建一个UsernamePasswordAuthenticationToken对象，用于认证
        Authentication authenticate = new UsernamePasswordAuthenticationToken(loginPrincipal, null, grantedAuthorities);
        //获取当前的SecurityContext
        SecurityContext context = SecurityContextHolder.getContext();
        //将认证信息设置到SecurityContext中
        context.setAuthentication(authenticate);
        //执行过滤器链
        filterChain.doFilter(request, response);
    }

    // 设置响应消息的编码格式
    public void responseMessage(HttpServletResponse response, ServiceCode serviceCode, String message) throws IOException {
        response.setContentType("application/json; charset=utf-8");
        // 获取响应输出流
        PrintWriter writer = response.getWriter();
        // 返回失败结果
        JsonResult<Void> jsonResult = JsonResult.fail(serviceCode, message);
        // 将结果转换成json字符串
        String jsonString = JSON.toJSONString(jsonResult);
        // 将json字符串写入响应输出流
        writer.println(jsonString);
        // 关闭响应输出流
        writer.close();
    }

}
